package com.todod.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.todod.common.util.SnowflakeIdGenerator;
import com.todod.dto.ApiDto;
import com.todod.dto.ProjectInfoDto;
import com.todod.entity.QueryProjectInfo;
import com.todod.mapper.CatalogDetailMapper;
import com.todod.mapper.ProjectInfoMapper;
import com.todod.model.CatalogDetail;
import com.todod.model.Dict;
import com.todod.model.ProjectFile;
import com.todod.model.ProjectInfo;
import com.todod.model.ProjectInfoRecords;

import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;

@Service
@Transactional
public class ProjectInfoService {

	@Autowired
	private ProjectInfoMapper projectInfoMapper;
	@Autowired
	private IDictService dictService;
	@Autowired
	public ProjectInfoRecordsService projectInfoRecordsService;
	@Autowired
	private CatalogDetailMapper catalogDetailMapper;
	@Autowired
	private ProjectFileService projectFileService;
	// 存储桶名称
	@Value("${minio.bucketName}")
	private String bucketName;
	@Resource
	private MinioClient client;

	/**
	 * 
	 * @param _userId
	 * @param queryProjectInfo
	 * @return
	 */
	public IPage<ProjectInfoDto> getListPage(Long _userId, QueryProjectInfo queryProjectInfo) {

		Page<ProjectInfoDto> page = new Page<>(queryProjectInfo.getPage(), queryProjectInfo.getLimit());

		IPage<ProjectInfoDto> iPage = projectInfoMapper.getListPage(page, _userId, queryProjectInfo);

		return iPage;
	}

	/**
	 * 
	 * @param _userId
	 * @param projectInfo
	 * @param value
	 * @param value2
	 */

	public void saveData(Long _userId, ProjectInfo projectInfo) {

		projectInfo.setId(SnowflakeIdGenerator.getId());
//		projectInfo.setStatus("1");

		projectInfo.setCreatetimeTi(LocalDateTime.now());
		projectInfo.setCreateuserId(_userId);
		projectInfoMapper.insert(projectInfo);
	}

	/**
	 * 
	 * @param _userId
	 * @param projectInfo
	 * @param value
	 * @param value2
	 */
	public void editData(Long _userId, ProjectInfo projectInfo) {

		ProjectInfo projectInfo1 = projectInfoMapper.selectById(projectInfo.getId());

		projectInfo.setCreatetimeTi(projectInfo1.getCreatetimeTi());
		projectInfo.setCreateuserId(projectInfo1.getCreateuserId());
		projectInfo.setModifytimeTi(LocalDateTime.now());
		projectInfo.setModifyuserId(_userId);
		projectInfoMapper.updateById(projectInfo);
	}

	/**
	 * 
	 * @param code
	 * @return
	 */
	public Map<String, Object> getInfo(Long code) {
		ProjectInfoDto projectInfo = projectInfoMapper.getInfo(code);
		// 获取审批记录数据
		List<ProjectInfoRecords> list = projectInfoRecordsService.getListByProjectId(code);

		Map<String, Object> map = new HashMap<>();
		map.put("projectInfo", projectInfo);
		map.put("projectInfoRecords", list);
		return map;
	}

	/**
	 * @Title: getSortNumByType
	 * @Description:根据项目类型获得项目编号
	 * @author: dyl
	 * @date 2020-03-15
	 * @param type
	 * @param departid
	 * @return
	 */
	public ProjectInfo getSortNumByType(String type, Long departid) {
		QueryWrapper<ProjectInfo> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("xg_type", type);
		queryWrapper.eq("xg_department_id", departid);
		queryWrapper.orderByDesc("xg_sort_num");
		queryWrapper.last("limit 1");

		ProjectInfo projectInfo = projectInfoMapper.selectOne(queryWrapper);

		return projectInfo;
	}

	/**
	 * 
	 * @param _userId
	 * @param string
	 */
	public void complete(Long _userId, String id) {
		ProjectInfo projectInfo = projectInfoMapper.selectById(Long.valueOf(id));

		projectInfo.setStatus("5");
		projectInfo.setModifytimeTi(LocalDateTime.now());
		projectInfo.setModifyuserId(_userId);
		projectInfoMapper.updateById(projectInfo);
	}

	/**
	 * 
	 * @param _userId
	 * @param string
	 */
	public void submit(Long _userId, String id) {
		ProjectInfo projectInfo = projectInfoMapper.selectById(Long.valueOf(id));

		projectInfo.setStatus("2");
		projectInfo.setModifytimeTi(LocalDateTime.now());
		projectInfo.setModifyuserId(_userId);
		projectInfoMapper.updateById(projectInfo);
	}

	/**
	 * 
	 * @param queryProjectInfo
	 * @return
	 */
	public List<ProjectInfo> getXiangMuList(Long companyId) {

		List<Dict> list = dictService.getListByParentCode("YW_XIANGMU_TYPE");

		List<ProjectInfo> list2 = new ArrayList<>();
		for (Dict dict : list) {
			ProjectInfo projectInfo = new ProjectInfo();

			projectInfo.setId(Long.valueOf(dict.getCode()));
			projectInfo.setXiangmumingcheng(dict.getName());
			projectInfo.setParentId(-1l);

//			QueryWrapper<ProjectInfo> queryWrapper = new QueryWrapper<>();
//			queryWrapper.eq("xg_type", dict.getCode());
//			queryWrapper.eq("xg_department_id", companyId);
//			queryWrapper.in("xg_status_k1", new Object[] { "3", "5" });
//			queryWrapper.orderByAsc("xg_sort_num");
//
//			List<ProjectInfo> list1 = projectInfoMapper.selectList(queryWrapper);
//
			List<ProjectInfo> list1 = projectInfoMapper.getXiangMuList(companyId, dict.getCode());
			for (ProjectInfo projectInfo1 : list1) {
				projectInfo1.setParentId(Long.valueOf(dict.getCode()));
				projectInfo1.setXiangmumingcheng(projectInfo1.getSortNum() + '-' + projectInfo1.getXiangmumingcheng());

				list2.add(projectInfo1);
			}

			list2.add(projectInfo);
		}

		return list2;
	}

	/**
	 * 
	 * @param queryProjectInfo
	 * @return
	 */
	public List<ApiDto> getXMList() {
		QueryWrapper<ProjectInfo> queryWrapper = new QueryWrapper<>();
		queryWrapper.in("xg_status_k1", new Object[] { "3", "5" });
		queryWrapper.orderByDesc("xg_sort_num");

		List<ProjectInfo> keys = projectInfoMapper.selectList(queryWrapper);

		List<ApiDto> list = new ArrayList<ApiDto>();
		for (ProjectInfo projectInfo : keys) {
			projectInfo.setXiangmumingcheng(projectInfo.getSortNum() + '-' + projectInfo.getXiangmumingcheng());

			ApiDto apiDto = new ApiDto();
			apiDto.setId(projectInfo.getId());
			apiDto.setTitle(projectInfo.getXiangmumingcheng());

			list.add(apiDto);
		}

		return list;
	}

	public List<ProjectInfo> getXMListByDpId(Long companyId) {
		QueryWrapper<ProjectInfo> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("xg_department_id", companyId);
		queryWrapper.eq("xg_status_k1", "3");
		queryWrapper.orderByDesc("xg_sort_num");

		List<ProjectInfo> list = projectInfoMapper.selectList(queryWrapper);

		for (ProjectInfo projectInfo : list) {
			projectInfo.setXiangmumingcheng(projectInfo.getSortNum() + '-' + projectInfo.getXiangmumingcheng());
		}

		return list;
	}

	public ProjectInfo getXMInfoByXmId(Long xmId) {
		QueryWrapper<ProjectInfo> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("xg_pk_id", xmId);

		ProjectInfo projectInfo = projectInfoMapper.selectOne(queryWrapper);

		return projectInfo;
	}

	public IPage<ProjectInfoDto> getYSPPage(Long _userId, QueryProjectInfo queryProjectInfo) {

		Page<ProjectInfoDto> page = new Page<>(queryProjectInfo.getPage(), queryProjectInfo.getLimit());
		IPage<ProjectInfoDto> iPage = projectInfoMapper.getYSPPage(page, _userId, queryProjectInfo);

		return iPage;
	}

	public void backTo(Long _userId, Long code) {

		ProjectInfo projectInfo = projectInfoMapper.selectById(code);

		projectInfo.setStatus("1");
		projectInfo.setModifytimeTi(LocalDateTime.now());
		projectInfo.setModifyuserId(_userId);
		projectInfoMapper.updateById(projectInfo);
	}

	public List<ProjectInfo> getAllList() {

		List<Dict> list = dictService.getListByParentCode("YW_XIANGMU_TYPE");

		List<ProjectInfo> list2 = new ArrayList<>();
		for (Dict dict : list) {
			ProjectInfo projectInfo = new ProjectInfo();

			projectInfo.setId(Long.valueOf(dict.getCode()));
			projectInfo.setTypeName(dict.getName());
			projectInfo.setParentId(-1l);

			QueryWrapper<ProjectInfo> queryWrapper = new QueryWrapper<>();
			queryWrapper.eq("xg_type", dict.getCode());
			queryWrapper.in("xg_status_k1", new Object[] { "3", "5" });
			queryWrapper.orderByAsc("xg_sort_num");

			List<ProjectInfo> list1 = projectInfoMapper.selectList(queryWrapper);

			if (list1 != null && list1.size() > 0) {
				List<ProjectInfo> list3 = new ArrayList<>();
				for (ProjectInfo projectInfo1 : list1) {
					projectInfo1.setParentId(Long.valueOf(dict.getCode()));
					projectInfo1.setTypeName(projectInfo1.getSortNum() + '-' + projectInfo1.getXiangmumingcheng());

					list3.add(projectInfo1);
				}

				projectInfo.setChildren(list3);

				list2.add(projectInfo);
			}
		}

		return list2;
	}

	public void getChildrens(HttpServletResponse response, Long xiangmuId, File rootFile) throws IOException {

		ProjectInfo projectInfo = projectInfoMapper.selectById(xiangmuId);

		QueryWrapper<CatalogDetail> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("cmd_classify_id", projectInfo.getCatalogId());
		queryWrapper.eq("cmd_parent_id", 1l);
		queryWrapper.eq("cmd_state", "1");
		queryWrapper.orderByAsc("cmd_sort_num");

		List<CatalogDetail> list = catalogDetailMapper.selectList(queryWrapper);

		for (CatalogDetail catalogDetail : list) {
			// 指定父路径和文件路径，创建一个二级File对象【只是创建了实例对象，并没有实际创建该目录】
			File secondLevelFile = new File(rootFile, catalogDetail.getTypeName());
			if (!secondLevelFile.exists()) {
				// 这才是实际创建了
				secondLevelFile.mkdirs();
			}

			List<ProjectFile> files = projectFileService.getListByParentIdAndXftId(xiangmuId, catalogDetail.getId());
			for (ProjectFile projectFile : files) {
				createFile(response, projectFile.getOriginalName(), projectFile.getRelativePath(), rootFile);
			}

			getNewChildren(response, xiangmuId, catalogDetail.getId(), secondLevelFile);
		}
	}

	private void getNewChildren(HttpServletResponse response, Long xiangmuId, Long parentId, File rootFile)
			throws IOException {

		QueryWrapper<CatalogDetail> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("cmd_parent_id", parentId);
		queryWrapper.eq("cmd_state", "1");
		queryWrapper.orderByAsc("cmd_sort_num");

		List<CatalogDetail> list = catalogDetailMapper.selectList(queryWrapper);

		for (CatalogDetail catalogDetail : list) {
			// 指定父路径和文件路径，创建一个二级File对象【只是创建了实例对象，并没有实际创建该目录】
			File secondLevelFile = new File(rootFile, catalogDetail.getTypeName());
			if (!secondLevelFile.exists()) {
				// 这才是实际创建了
				secondLevelFile.mkdirs();
			}

			List<ProjectFile> files = projectFileService.getListByParentIdAndXftId(xiangmuId, catalogDetail.getId());
			for (ProjectFile projectFile : files) {
				createFile(response, projectFile.getOriginalName(), projectFile.getRelativePath(), secondLevelFile);
			}

			getNewChildren(response, xiangmuId, catalogDetail.getId(), secondLevelFile);
		}
	}

	private void createFile(HttpServletResponse response, String originalName, String fileName, File secondLevelFile) {

		InputStream inStream = null;
		try {
			inStream = client.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
			// 防止乱码
			response.setHeader("Content-Disposition",
					"attachment;filename=" + URLEncoder.encode(originalName, "UTF-8"));
			response.setContentType("application/force-download");
			response.setCharacterEncoding("UTF-8");
			String pdfPath = secondLevelFile + File.separator + originalName;

			FileCopyUtils.copy(inStream, new FileOutputStream(pdfPath));

		} catch (InvalidKeyException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (ErrorResponseException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (InsufficientDataException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (InternalException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (InvalidResponseException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (NoSuchAlgorithmException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (ServerException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (XmlParserException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IllegalArgumentException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} finally {
			if (inStream != null) {
				try {
					inStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 压缩的递归方法
	 *
	 * @param sourceFile       源文件
	 * @param zos              zip输出流
	 * @param fileName         源文件的名称
	 * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
	 *                         false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
	 */
	private static void compress(File sourceFile, ZipOutputStream zos, String fileName, boolean keepDirStructure)
			throws IOException {
		byte[] buf = new byte[2 * 1024];
		// 判断是否是一个文件
		if (sourceFile.isFile()) {
			// 向zip输出流中添加一个zip实体，构造器中name为zip实体的文件的名字
			zos.putNextEntry(new ZipEntry(fileName));
			// 创建文件（即某张图片）的输入流
			try (FileInputStream in = new FileInputStream(sourceFile)) {

				int len;
				// read方法：每调用一次就从FileInputStream流中读取一个字节，并返回下一个数据字节，若已到达末尾，就返回-1。
				while ((len = in.read(buf, 0, buf.length)) != -1) {
					zos.write(buf, 0, len);
				}
				// 实际写入到了zip输出流的zip实体中，还没写到文件中【zip文件时0kb，不能打开，因为流没有关闭】
				zos.closeEntry();
				in.close();
			} catch (IOException e) {
				throw new IOException(e);
			}
		} else {
			// 源文件时目录
			// 获取该目录下所有文件和目录的绝对路径
			File[] listFiles = sourceFile.listFiles();
			// 空目录
			if (listFiles == null || listFiles.length == 0) {
				// 需要保留原来的文件结构时,需要对空文件夹进行处理
				if (keepDirStructure) {
					// 空文件夹的处理
					zos.putNextEntry(new ZipEntry(fileName + "/"));
					// 没有文件，不需要文件的copy
					zos.closeEntry();
				}
			} else {
				// 非空目录
				for (File file : listFiles) {
					if (keepDirStructure) {
						// 注意：getName()仅得到最后一层的名字，不是路径，所以要加“/”，不然所有文件都跑到压缩包根目录下了
						compress(file, zos, fileName + "/" + file.getName(), true);
					} else {
						compress(file, zos, file.getName(), false);
					}
				}
			}
		}
	}

	/**
	 * 压缩成ZIP 方法1：保留多级目录结构
	 *
	 * @param sourceFile       照片存放路径
	 * @param out              压缩文件输出流
	 * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
	 *                         false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
	 */
	public void toZip1(File sourceFile, OutputStream out, boolean keepDirStructure) throws IOException {
		long start = System.currentTimeMillis();
		// 创建压缩输出流,java7的新语法：Try-with-resources，会确保异常抛出或者try代码块结束时close流【该流必须实现AutoCloseable类】（若是java7之前的版本，则不会生效）
		try (ZipOutputStream zos = new ZipOutputStream(out)) {
			// 压缩
			compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
			zos.close();
			long end = System.currentTimeMillis();
			System.out.println("压缩完成，耗时：" + (end - start) + " ms");
		} catch (IOException e) {
			throw new IOException(e);
		}
	}

	/**
	 * 压缩成ZIP 方法2：所有文件压缩到一个目录下
	 *
	 * @param srcFiles 需要压缩的文件列表
	 * @param zos      压缩文件的输出流
	 */
	public void toZip2(List<File> srcFiles, ZipOutputStream zos) throws IOException {
		long start = System.currentTimeMillis();
		// 压缩文件的输出流
		for (File srcFile : srcFiles) {
			byte[] buf = new byte[2 * 1024];
			zos.putNextEntry(new ZipEntry(srcFile.getName()));
			try (FileInputStream in = new FileInputStream(srcFile)) {
				int len;
				while ((len = in.read(buf)) != -1) {
					zos.write(buf, 0, len);
				}
				zos.closeEntry();
			} catch (IOException e) {
				throw new IOException(e);
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("压缩完成，耗时：" + (end - start) + " ms");
	}

	/**
	 * 删除文件夹
	 *
	 * @param folderPath 文件夹完整绝对路径
	 */
	public void delFolder(String folderPath) {
		try {
			// 删除目录下所有内容
			delAllFile(folderPath);
			File myFilePath = new File(folderPath);
			// 删除空文件夹
			myFilePath.delete();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 删除指定文件夹下所有文件
	 *
	 * @param path 文件夹完整绝对路径
	 */
	public boolean delAllFile(String path) {
		boolean bea = false;
		File file = new File(path);
		if (!file.exists()) {
			return bea;
		}
		if (!file.isDirectory()) {
			return bea;
		}
		//
		String[] tempList = file.list();
		File temp;
		if (tempList != null) {
			for (String var : tempList) {
				// separator 代替文件或文件夹路径的斜线或反斜线，防止跨平台出现错误
				if (path.endsWith(File.separator)) {
					temp = new File(path + var);
				} else {
					temp = new File(path + File.separator + var);
				}
				if (temp.isFile()) {
					temp.delete();
				}
				if (temp.isDirectory()) {
					// 先删除文件夹里面的文件
					delAllFile(path + "/" + var);
					// 再删除空文件夹
					delFolder(path + "/" + var);
					bea = true;
				}
			}
		}
		return bea;
	}

}
